package com.wang.tool.monitor.fastover;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONPath;
import com.alibaba.fastjson.TypeReference;
import com.wang.tool.compile.CacheableDynamicCompiler;
import com.wang.tool.monitor.ops.SimpleOps;
import com.wang.tool.util.CommonUtil;
import com.wang.tool.common.SimpleException;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 快速结束配置
 */
public class FastoverConfig {

    /**
     * 开始时间
     */
    private Long beginTime;

    /**
     * 结束时间
     */
    private Long endTime;

    /**
     * 参数源
     * 仅在某条件下进行fastover
     */
    private List<String> sources;

    /**
     * 白名单
     * 仅在某条件下进行fastover
     */
    private Set<String> whites;


    /**
     * fastFail信息
     * 非空代表抛异常,非空时数组必须是俩属性
     * 1=errorCode,2=errorMsg(可为null,errorCode兜底)
     */
    private String[] fastFail;

    /**
     * fastReturn信息
     * 非空代表指定返回结果,非空时数组必须是俩属性
     * 1=resultClassName,2=serializeInfo
     * 基本类型自处理
     * 常用list,map等泛型方式
     * 动态编译的TypeReference支持动态泛型
     * FastReturnDeCodec可提供定制解析器
     *
     * @see FastReturnDeCodec
     */
    private Object[] fastReturn;

    /**
     * 解析快速结束
     *
     * @param fastoverConfig
     * @return
     */
    public static FastoverFunction parseFastoverFunction(FastoverConfig fastoverConfig) {
        if (fastoverConfig == null) {
            return null;
        }
        Long beginTime = fastoverConfig.getBeginTime();
        Long endTime = fastoverConfig.getEndTime();
        //已经结束了
        if (endTime != null && endTime < System.currentTimeMillis()) {
            return null;
        }
        if (beginTime != null && endTime != null && beginTime > endTime) {
            return null;
        }

        Set<String> whites = fastoverConfig.getWhites();
        List<String> sources = fastoverConfig.getSources();
        List<JSONPath> parsedSources = new ArrayList<>();
        if (CommonUtil.isNotEmpty(whites)) {
            if (CommonUtil.isEmpty(sources)) {
                return null;
            }
            parsedSources.addAll(sources.stream().map(s->{
                JSONPath compile = JSONPath.compile(s);
                //测试
                compile.eval(new Object[]{});
                return compile;
            }).collect(Collectors.toList()));
        }


        String[] fastFail = fastoverConfig.getFastFail();
        //快速异常
        if (fastFail != null) {
            if (fastFail.length != 2) {
                throw new IllegalArgumentException("fastFail length must be 2");
            }
            String errorCode = fastFail[0];
            if (errorCode == null) {
                throw new IllegalArgumentException("errorCode can not be null");
            }
            String errorMsg = fastFail[1] == null ? errorCode : fastFail[1];
            FastoverInfo fastFailInfo = FastoverInfo.fastFail(new SimpleException(errorCode, errorMsg));
            return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastFailInfo : null;
        }

        Object[] fastReturn = fastoverConfig.getFastReturn();
        if (fastReturn == null) {
            return null;
        }

        //快速结果
        //快速返回结果(简易模型)
        if (fastReturn.length != 2 || fastReturn[0] == null) {
            throw new IllegalArgumentException("fastReturn length must be 2 and returnType can not be null");
        }
        String returnTypeStr = fastReturn[0].toString().trim();
        //去空格,去特殊
        try {
            //处理小类型
            String returnValStr = fastReturn[1] == null ? null : fastReturn[1].toString().trim();
            if (String.class.getSimpleName().equals(returnTypeStr) || String.class.getName().equals(returnTypeStr)) {
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(returnValStr);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (void.class.getSimpleName().equals(returnTypeStr) || Void.class.getName().equals(returnTypeStr)) {
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(null);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (Integer.class.getName().equals(returnTypeStr)) {
                Integer val = CommonUtil.isBlank(returnValStr) ? null : Integer.parseInt(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (int.class.getSimpleName().equals(returnTypeStr)) {
                int val = CommonUtil.isBlank(returnValStr) ? 0 : Integer.parseInt(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (Boolean.class.getName().equals(returnTypeStr)) {
                if (CommonUtil.isNotBlank(returnValStr) && !returnValStr.equals("true") && !returnValStr.equals("false")) {
                    throw new IllegalArgumentException("fastReturn booleanStr must be true/false");
                }
                Boolean val = CommonUtil.isBlank(returnValStr) ? null : Boolean.parseBoolean(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (boolean.class.getSimpleName().equals(returnTypeStr)) {
                if (CommonUtil.isNotBlank(returnValStr) && !returnValStr.equals("true") && !returnValStr.equals("false")) {
                    throw new IllegalArgumentException("fastReturn booleanStr must be true/false");
                }
                boolean val = CommonUtil.isNotBlank(returnValStr) && Boolean.parseBoolean(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (Double.class.getName().equals(returnTypeStr)) {
                Double val = CommonUtil.isBlank(returnValStr) ? null : Double.parseDouble(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (double.class.getSimpleName().equals(returnTypeStr)) {
                double val = CommonUtil.isBlank(returnValStr) ? 0.0D : Double.parseDouble(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (Long.class.getName().equals(returnTypeStr)) {
                Long val = CommonUtil.isBlank(returnValStr) ? null : Long.parseLong(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (long.class.getSimpleName().equals(returnTypeStr)) {
                long val = CommonUtil.isBlank(returnValStr) ? 0 : Long.parseLong(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (Byte.class.getName().equals(returnTypeStr)) {
                Byte val = CommonUtil.isBlank(returnValStr) ? null : Byte.parseByte(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (byte.class.getSimpleName().equals(returnTypeStr)) {
                byte val = CommonUtil.isBlank(returnValStr) ? 0 : Byte.parseByte(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (Short.class.getName().equals(returnTypeStr)) {
                Short val = CommonUtil.isBlank(returnValStr) ? null : Short.parseShort(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (short.class.getSimpleName().equals(returnTypeStr)) {
                short val = CommonUtil.isBlank(returnValStr) ? 0 : Short.parseShort(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (Float.class.getName().equals(returnTypeStr)) {
                Float val = CommonUtil.isBlank(returnValStr) ? null : Float.parseFloat(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (float.class.getSimpleName().equals(returnTypeStr)) {
                float val = CommonUtil.isBlank(returnValStr) ? 0.0F : Float.parseFloat(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (Character.class.getName().equals(returnTypeStr)) {
                if (CommonUtil.isNotBlank(returnValStr) && returnValStr.length() != 1) {
                    throw new IllegalArgumentException("str size  can not larger 1");
                }
                Character val = CommonUtil.isBlank(returnValStr) ? null : returnValStr.charAt(0);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (char.class.getSimpleName().equals(returnTypeStr)) {
                if (CommonUtil.isNotBlank(returnValStr) && returnValStr.length() != 1) {
                    throw new IllegalArgumentException("str size  can not larger 1");
                }
                char val = CommonUtil.isBlank(returnValStr) ? '\u0000' : returnValStr.charAt(0);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(val);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }

            //处理对象,读取方式和小类型不一样(json化)
            returnValStr = fastReturn[1] == null ? null : JSON.toJSONString(fastReturn[1]).trim().equals("null") ? null : JSON.toJSONString(fastReturn[1]).trim();
            //空字符串 返回null
            if (CommonUtil.isBlank(returnValStr)) {
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(null);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }

            //常用泛型
            //list
            if (returnTypeStr.matches("^List<([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+>$")) {
                Class t = Class.forName(returnTypeStr.substring(returnTypeStr.indexOf("<") + 1, returnTypeStr.indexOf(">")));
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(FastoverConfig.parseToList(returnValStr, t));
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (returnTypeStr.matches("^ArrayList<([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+>$")) {
                Class t = Class.forName(returnTypeStr.substring(returnTypeStr.indexOf("<") + 1, returnTypeStr.indexOf(">")));
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(FastoverConfig.parseToArrayList(returnValStr, t));
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (returnTypeStr.matches("^LinkedList<([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+>$")) {
                Class t = Class.forName(returnTypeStr.substring(returnTypeStr.indexOf("<") + 1, returnTypeStr.indexOf(">")));
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(FastoverConfig.parseToLinkedList(returnValStr, t));
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }

            //set
            if (returnTypeStr.matches("^Set<([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+>$")) {
                Class t = Class.forName(returnTypeStr.substring(returnTypeStr.indexOf("<") + 1, returnTypeStr.indexOf(">")));
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(FastoverConfig.parseToSet(returnValStr, t));
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (returnTypeStr.matches("^HashSet<([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+>$")) {
                Class t = Class.forName(returnTypeStr.substring(returnTypeStr.indexOf("<") + 1, returnTypeStr.indexOf(">")));
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(FastoverConfig.parseToHashSet(returnValStr, t));
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (returnTypeStr.matches("^LinkedHashSet<([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+>$")) {
                Class t = Class.forName(returnTypeStr.substring(returnTypeStr.indexOf("<") + 1, returnTypeStr.indexOf(">")));
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(FastoverConfig.parseToLinkedHashSet(returnValStr, t));
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }

            //map
            if (returnTypeStr.matches("^Map<([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+,([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+>$")) {
                String types = returnTypeStr.substring(returnTypeStr.indexOf("<") + 1, returnTypeStr.indexOf(">"));
                Class t1 = Class.forName(types.split(",")[0].trim());
                Class t2 = Class.forName(types.split(",")[1].trim());
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(FastoverConfig.parseToMap(returnValStr, t1, t2));
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (returnTypeStr.matches("^HashMap<([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+,([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+>$")) {
                String types = returnTypeStr.substring(returnTypeStr.indexOf("<") + 1, returnTypeStr.indexOf(">"));
                Class t1 = Class.forName(types.split(",")[0].trim());
                Class t2 = Class.forName(types.split(",")[1].trim());
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(FastoverConfig.parseToHashMap(returnValStr, t1, t2));
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (returnTypeStr.matches("^LinkedHashMap<([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+,([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+>$")) {
                String types = returnTypeStr.substring(returnTypeStr.indexOf("<") + 1, returnTypeStr.indexOf(">"));
                Class t1 = Class.forName(types.split(",")[0].trim());
                Class t2 = Class.forName(types.split(",")[1].trim());
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(FastoverConfig.parseToLinkedHashMap(returnValStr, t1, t2));
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }
            if (returnTypeStr.matches("^TreeMap<([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+,([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+>$")) {
                String types = returnTypeStr.substring(returnTypeStr.indexOf("<") + 1, returnTypeStr.indexOf(">"));
                Class t1 = Class.forName(types.split(",")[0].trim());
                Class t2 = Class.forName(types.split(",")[1].trim());
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(FastoverConfig.parseToTreeMap(returnValStr, t1, t2));
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }

            //自定义泛型
            if (returnTypeStr.matches("^([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+<([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+(,([a-zA-Z_]\\w*)+([.][a-zA-Z_]\\w*)+)*>$")) {
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(FastoverConfig.parseToGenericObj(returnValStr, returnTypeStr));
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }


            Class<?> resultType = Class.forName(returnTypeStr);
            //自定制反序列化
            if (FastReturnDeCodec.class.isAssignableFrom(resultType)) {
                //自定义可解决疑难杂症
                Object result = ((FastReturnDeCodec) resultType.newInstance()).decode(returnValStr);
                FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(result);
                return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
            }

            //普通类型,json反序列化
            Object result = JSON.parseObject(returnValStr, resultType);
            FastoverInfo fastReturnInfo = FastoverInfo.fastReturn(result);
            return (time, params) -> isOpen(time, params, beginTime, endTime, parsedSources, whites) ? fastReturnInfo : null;
        } catch (Throwable e) {
            throw new IllegalArgumentException("resultInstance is error, first is resultClassName and second is serialize info , fastReturn:" + Arrays.toString(fastReturn) + ", errorMsg:" + e.getMessage());
        }
    }

    private static boolean isOpen(long time, Object[] params, Long beginTime, Long endTime, List<JSONPath> compiledSources, Set<String> whites) {
        if (beginTime != null && time < beginTime) {
            return false;
        }

        if (endTime != null && time > endTime) {
            return false;
        }

        //没有白名单配置
        if (compiledSources.isEmpty()) {
            return true;
        }
        for (JSONPath source : compiledSources) {
            Object val = source.eval(params);
            if (val == null) {
                continue;
            }
            //集合
            if (val instanceof Collection) {
                if(((Collection<?>) val).stream()
                        .anyMatch(s -> whites.contains(String.valueOf(s)))){
                    return true;
                }
            }else if(whites.contains(val.toString())){
                return true;
            }
        }
        return false;
    }

    private static <T> List<T> parseToList(String json, Class<T> type) {
        return JSON.parseObject(json,
                new TypeReference<List<T>>(type) {
                });
    }

    private static <T> ArrayList<T> parseToArrayList(String json, Class<T> type) {
        return JSON.parseObject(json,
                new TypeReference<ArrayList<T>>(type) {
                });
    }


    private static <T> LinkedList<T> parseToLinkedList(String json, Class<T> type) {
        return JSON.parseObject(json,
                new TypeReference<LinkedList<T>>(type) {
                });
    }

    private static <T> Set<T> parseToSet(String json, Class<T> type) {
        return JSON.parseObject(json,
                new TypeReference<Set<T>>(type) {
                });
    }

    private static <T> HashSet<T> parseToHashSet(String json, Class<T> type) {
        return JSON.parseObject(json,
                new TypeReference<HashSet<T>>(type) {
                });
    }

    private static <T> LinkedHashSet<T> parseToLinkedHashSet(String json, Class<T> type) {
        return JSON.parseObject(json,
                new TypeReference<LinkedHashSet<T>>(type) {
                });
    }


    private static <K, V> Map<K, V> parseToMap(String json, Class<K> type, Class<V> type2) {
        return JSON.parseObject(json,
                new TypeReference<Map<K, V>>(type, type2) {
                });
    }

    private static <K, V> HashMap<K, V> parseToHashMap(String json, Class<K> type, Class<V> type2) {
        return JSON.parseObject(json,
                new TypeReference<HashMap<K, V>>(type, type2) {
                });
    }

    private static <K, V> LinkedHashMap<K, V> parseToLinkedHashMap(String json, Class<K> type, Class<V> type2) {
        return JSON.parseObject(json,
                new TypeReference<LinkedHashMap<K, V>>(type, type2) {
                });
    }

    private static <K, V> TreeMap<K, V> parseToTreeMap(String json, Class<K> type, Class<V> type2) {
        return JSON.parseObject(json,
                new TypeReference<TreeMap<K, V>>(type, type2) {
                });
    }


    /**
     * 使用动态编译器编译自动拼接的TypeReference子类再使用反序列化
     *
     * @param json
     * @param typeStr
     * @return
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    private static Object parseToGenericObj(String json, String typeStr) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //验证下
        Class<?> mainType = Class.forName(typeStr.substring(0, typeStr.indexOf("<")));
        Arrays.stream(typeStr.substring(typeStr.indexOf("<") + 1, typeStr.indexOf(">"))
                        .split(","))
                .forEach(s -> {
                    try {
                        Class.forName(s.trim());
                    } catch (Exception e) {
                        throw new IllegalArgumentException("generic type:" + s + " is not found");
                    }
                });

        String clsStr = SimpleOps.class.getPackage().toString() + ";\n" +
                "public class DynamicTypeReference$" + mainType.getSimpleName() + "$ extends com.alibaba.fastjson.TypeReference<" + typeStr + "> {\n" +
                "}";

        TypeReference<?> dynamicTypeReference = (TypeReference<?>) CacheableDynamicCompiler.getOrCompile(typeStr, clsStr).newInstance();
        return JSON.parseObject(json, dynamicTypeReference);
    }


    public List<String> getSources() {
        return sources;
    }

    public void setSources(List<String> sources) {
        this.sources = sources;
    }

    public Set<String> getWhites() {
        return whites;
    }

    public void setWhites(Set<String> whites) {
        this.whites = whites;
    }

    public String[] getFastFail() {
        return fastFail;
    }

    public void setFastFail(String[] fastFail) {
        this.fastFail = fastFail;
    }

    public Object[] getFastReturn() {
        return fastReturn;
    }

    public void setFastReturn(Object[] fastReturn) {
        this.fastReturn = fastReturn;
    }

    public Long getBeginTime() {
        return beginTime;
    }

    public void setBeginTime(Long beginTime) {
        this.beginTime = beginTime;
    }

    public Long getEndTime() {
        return endTime;
    }

    public void setEndTime(Long endTime) {
        this.endTime = endTime;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        FastoverConfig that = (FastoverConfig) o;
        return Objects.equals(beginTime, that.beginTime) && Objects.equals(endTime, that.endTime) && Objects.equals(sources, that.sources) && Objects.equals(whites, that.whites) && Arrays.equals(fastFail, that.fastFail) && Arrays.equals(fastReturn, that.fastReturn);
    }

    @Override
    public int hashCode() {
        int result = Objects.hash(beginTime, endTime, sources, whites);
        result = 31 * result + Arrays.hashCode(fastFail);
        result = 31 * result + Arrays.hashCode(fastReturn);
        return result;
    }

    @Override
    public String toString() {
        return "FastoverConfig{" +
                "beginTime=" + beginTime +
                ", endTime=" + endTime +
                ", sources=" + sources +
                ", whites=" + whites +
                ", fastFail=" + Arrays.toString(fastFail) +
                ", fastReturn=" + Arrays.toString(fastReturn) +
                '}';
    }
}
